package mongodb.general.operator.nodb.services.impl;

import com.alibaba.fastjson.JSONObject;
import mongodb.general.operator.nodb.services.CommMongoDBInfoService;
import mongodb.general.operator.nodb.utils.CommonMongoHandleUtils;
import mongodb.general.operator.nodb.utils.ReqHandleUtils;
import mongodb.general.operator.nodb.utils.ResHandleUtils;
import mongodb.general.operator.nodb.vo.MongoRequestParamsHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author YCWB0354
 * @since 2021/12/5 20:35:03
 */
@Service
public class CommMongoDBInfoServiceImpl implements CommMongoDBInfoService {
    //log生成
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    MongoTemplate mongotemplate;

    /**
     * 信息追加
     *
     * @param infoDto
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Object insert(JSONObject infoDto) throws Exception {
        try {
            String tableName = ReqHandleUtils.preReqOperator(infoDto,true);
            //大宽表存储
            mongotemplate.insert(infoDto, tableName);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("新增数据失败. error:{}", e.getMessage());
            //添加错误信息
            throw new Exception("新增数据失败" + e.getMessage());
        }
        return true;
    }

    /**
     * 信息修改(如果是单个属性就更新,如果是事件就新增或者更新)
     *
     * @param infoDto
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Object update(JSONObject infoDto) throws Exception {
        try {
            Map<String, Object> entity = (Map) infoDto;
            String tableName =  ReqHandleUtils.preReqOperator(infoDto,false);
            //找到数据库的对应人员
            Query queryById = Query.query(Criteria.where("_id").is(entity.get("_id")).and("delFlag").is(0));
            Map<String, Object> dataFromDB = (Map<String, Object>) mongotemplate.findOne(queryById, Object.class, tableName);
            //定位和更新基础信息
            Query query = new Query(Criteria.where("_id").is(dataFromDB.get("_id")));
            Update update = new Update();
            //attribute人员属性
            for (String attribute : entity.keySet()) {
                //外层_id不做更新操作
                if(attribute.equals("_id")){
                    continue;
                }
                //基础信息更新
                update.set(attribute, entity.get(attribute));
            }
            //基础信息更新
            mongotemplate.updateFirst(query, update, tableName);
            return "操作成功";
        } catch (Exception e) {
            throw new Exception(e);
        }
    }
    /**
     * 逻辑删除 将delFlag置为2
     * @param infoDto
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Object remove(JSONObject infoDto) throws Exception {
        try {
            Map<String, Object> infoMap = (Map) infoDto;
            MongoRequestParamsHandler paramsHandler = new MongoRequestParamsHandler(infoMap);
            Map<String, Object> paramsMap = paramsHandler.getParamsMap();
            Query query = Query.query(Criteria.where("_id").is(paramsMap.get("_id")));
            return mongotemplate.updateFirst(query, Update.update("delFlag", 2), paramsHandler.getTableName());
        } catch (Exception e) {
            throw new Exception(e);
        }

    }

    /**
     * 主键获取信息
     *
     * @param infoDto
     * @return
     * @throws Exception
     */
    @Override
    public Object getById(JSONObject infoDto) throws Exception {
        try {
            Map<String, Object> infoMap = (Map<String, Object>) infoDto;
            MongoRequestParamsHandler paramsHandler = new MongoRequestParamsHandler(infoDto);
            Query query = CommonMongoHandleUtils.getQuery(paramsHandler);
            String tableName = paramsHandler.getTableName();
            Object resultData = null;
            String eventNameUnwind = paramsHandler.getEventNameUnwind();
            // 判断是否需要聚合操作 排除子数组中不满足条件的数据（如果用聚合操作，嵌套子数组只要有一条记录满足条件，将返回整个数组）
            if (eventNameUnwind != null) {
                List<AggregationOperation> aggregationOperationList = new ArrayList<>();
                // $unwind操作将数组展开
                aggregationOperationList.add(Aggregation.unwind(eventNameUnwind));
                // $match匹配满足条件的数据
                aggregationOperationList.add(Aggregation.match(MongoRequestParamsHandler.getCriteriaFromQuery(query)));
                // 如果需要，根据数组字段排序 传Sort.unsorted()会报错
                if (paramsHandler.getSortObject() != Sort.unsorted()) {
                    aggregationOperationList.add(Aggregation.sort(paramsHandler.getSortObject()));
                }
                // $group操作，重新组合成一条记录
                aggregationOperationList.add(Aggregation.group("_id").push(eventNameUnwind).as(eventNameUnwind));
                Aggregation aggregation = Aggregation.newAggregation(aggregationOperationList);

                AggregationResults<Object> aggregationResults = mongotemplate.aggregate(aggregation, paramsHandler.getTableName(), Object.class);
                List<Object> mappedResults = aggregationResults.getMappedResults();
                if (!mappedResults.isEmpty()) {
                    resultData = mappedResults.get(0);
                }
            } else {
                resultData = mongotemplate.findOne(query, Object.class, tableName);
            }
            ResHandleUtils.transObjectIdToString(resultData);
            return resultData;
        } catch (Exception e) {
            throw new Exception(e);
        }
    }

    /**
     * 根据请求参数的条件查询
     *
     * @param infoDto
     * @return
     * @throws Exception
     */
    @Override
    public List<Object> getByParams(JSONObject infoDto) throws Exception {
        try {
            // 把请求参数封装成对象
            MongoRequestParamsHandler paramsHandler = new MongoRequestParamsHandler(infoDto);
            Query query = CommonMongoHandleUtils.getQuery(paramsHandler);
            logger.debug("mongo-getByParams查询参数:{}", query);
            List<Object> resultList = mongotemplate.find(query, Object.class, paramsHandler.getTableName());
            // 将ObjectId对象转为String
            ResHandleUtils.transObjectIdToStrings(resultList);
            return resultList;
        } catch (Exception e) {
            throw new Exception(e);
        }
    }
    /**
     * 分页查询
     *
     * @param infoDto
     * @return
     * @throws Exception
     */
    @Override
    public Page<Object> page(JSONObject infoDto) throws Exception {
        MongoRequestParamsHandler paramsHandler = new MongoRequestParamsHandler(infoDto);
        Query query = CommonMongoHandleUtils.getQuery(paramsHandler);
        if (logger.isDebugEnabled()) {
            logger.debug("mongo-page查询参数:{}", query);
        }
        System.out.println("mongo-page查询参数: " + query);
        return pageQuery(query, paramsHandler.getPageSize(), paramsHandler.getCurrentPage(), paramsHandler.getTableName());
    }

    private Page<Object> pageQuery(Query query, Integer size, Integer current, String tableName) {
        Pageable pageable = PageRequest.of(current - 1, size);
        query.with(pageable);
        List<Object> resultList = mongotemplate.find(query, Object.class, tableName);
        ResHandleUtils.transObjectIdToString(resultList);
        return PageableExecutionUtils.getPage(resultList,
                pageable,
                () -> mongotemplate.count(query.limit(-1).skip(-1), null, tableName));
    }
}
